package com.zjk.toolkit.dynamic.thread.pool.sdk.config;

import com.zjk.toolkit.dynamic.thread.pool.sdk.domain.DyThreadPoolService;
import com.zjk.toolkit.dynamic.thread.pool.sdk.domain.IDyThreadPoolService;
import com.zjk.toolkit.dynamic.thread.pool.sdk.domain.entity.ThreadPoolEntity;
import com.zjk.toolkit.dynamic.thread.pool.sdk.domain.enums.RegistryEnum;
import com.zjk.toolkit.dynamic.thread.pool.sdk.registry.IRegistry;
import com.zjk.toolkit.dynamic.thread.pool.sdk.registry.redis.RedisRegistry;
import com.zjk.toolkit.dynamic.thread.pool.sdk.trigger.job.ThreadPoolReportJob;
import com.zjk.toolkit.dynamic.thread.pool.sdk.trigger.listener.ThreadPoolConfigAdjustListener;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableConfigurationProperties(DyThreadPoolProperties.class)
@EnableScheduling
public class DyThreadPoolAutoConfig {
    private final Logger logger = LoggerFactory.getLogger(DyThreadPoolAutoConfig.class);

    private String applicationName;

    @Bean(name = "dyThreadPoolAutoConfigService")
    public DyThreadPoolService DyThreadPoolAutoConfigService(ApplicationContext applicationContext, Map<String, ThreadPoolExecutor> threadPoolExecutorMap, RedissonClient redissonClient) {
        String applicationName = applicationContext.getEnvironment().getProperty("spring.application.name");
        if (StringUtils.isBlank(applicationName)) {
            logger.warn("动态线程池启动警告,applicationName未配置,无法获取spring.application.name!");
        }
        Set<String> threadPoolKeys = threadPoolExecutorMap.keySet();
        for(String threadPoolKey : threadPoolKeys) {
            ThreadPoolEntity threadPoolEntit = redissonClient.<ThreadPoolEntity>getBucket(
                    RegistryEnum.THREAD_POOL_CONFIG_PARAMETER_LIST_KEY.getKey()
                            + "_"+ applicationName+"_"+ threadPoolKey).get();
            if (threadPoolEntit != null) {
                ThreadPoolExecutor threadPoolExecutor = threadPoolExecutorMap.get(threadPoolKey);
                threadPoolExecutor.setCorePoolSize(threadPoolEntit.getCorePoolSize());
                threadPoolExecutor.setMaximumPoolSize(threadPoolEntit.getMaximumPoolSize());
            }
        }
        return new DyThreadPoolService(applicationName, threadPoolExecutorMap);
    }

    @Bean(name = "dyThreadRedissonClient")
    public RedissonClient redissonClient(DyThreadPoolProperties properties) {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://" + properties.getHost() + ":" + properties.getPort())
                .setPassword(properties.getPassword())
                .setConnectionPoolSize(properties.getPoolSize())
                .setConnectionMinimumIdleSize(properties.getMinIdleSize())
                .setIdleConnectionTimeout(properties.getIdleTimeout())
                .setConnectTimeout(properties.getConnectTimeout())
                .setRetryAttempts(properties.getRetryAttempts())
                .setRetryInterval(properties.getRetryInterval())
                .setPingConnectionInterval(properties.getPingInterval())
                .setKeepAlive(properties.isKeepAlive());

        RedissonClient redissonClient = Redisson.create(config);
        logger.info("动态线程池，注册器（redis）链接初始化完成。{} {} {}", properties.getHost(), properties.getPoolSize(), !redissonClient.isShutdown());
        return redissonClient;
    }

    /**
     * 目前使用redis注册中心，后续可以实现其他方式，实现IRegistry接口即可
     * @param dyThreadRedissonClient
     * @return
     */
    @Bean
    public IRegistry redisRegistry(RedissonClient dyThreadRedissonClient) {
        return new RedisRegistry(dyThreadRedissonClient);
    }

    @Bean
    public ThreadPoolReportJob threadPoolReportJob(IDyThreadPoolService dyThreadPoolService, IRegistry redisRegistry) {
        return new ThreadPoolReportJob(redisRegistry, dyThreadPoolService);
    }

    @Bean
    public ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener(IDyThreadPoolService dyThreadPoolService, IRegistry redisRegistry) {
        return new ThreadPoolConfigAdjustListener(dyThreadPoolService,redisRegistry );
    }

    @Bean(name = "dyThreadPoolRedisTopic")
    public RTopic threadPoolConfigAdjustListener(RedissonClient redissonClient,ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener) {
        RTopic topic = redissonClient.getTopic(RegistryEnum.DYNAMIC_THREAD_POOL_REDIS_TOPIC.getKey()+ "_" + applicationName);
        topic.addListener(ThreadPoolEntity.class, threadPoolConfigAdjustListener);
        return topic;
    }
}
